﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ElmahCore.Mvc;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using net.xBei.Configurations;
using net.xBei.Helper;
using net.xBei.WebApi.Configurations;
using net.xBei.WebApi.FrameworkExtensions;
using net.xBei.WebApi.Helper;
using net.xBei.WebApi.Repository;
using xBei.Redis.Extension;

namespace net.xBei.WebApi {
    /// <summary>
    /// API服务
    /// </summary>
    public static partial class WebApplication {
        /// <summary>
        /// 
        /// </summary>
        public static IWebHostEnvironment Environment { get; private set; } = default!;

        /// <summary>
        /// WebAPI服务启动入口
        /// <list type="bullet">
        /// <item>配置API版本支持,<see cref="ApiVersioningExtensions.SetupApiVersioning(IServiceCollection)"/></item>
        /// <item>配置JWT认证<see cref="JwtExtensions.SetupJwt(IServiceCollection, IConfigurationRoot, Action{IServiceCollection}?)"/>，配置：<see cref="JwtSettings"/></item>
        /// <item>注入<see cref="RedisClient"/>服务，配置：<see cref="RedisSettings"/></item>
        /// <item>注入<see cref="ApiCallerFactory"/>服务，配置：<see cref="ApiCallerSettings"/></item>
        /// <item>自动（<see cref="StartOptions.AutoRegisterServices"/>==true）注入<see cref="IAutoRegisterRepository"/>、<see cref="IAutoRegisterSingleton"/>，入口程序集已经自动加载</item>
        /// </list>
        /// </summary>
        /// <param name="args">命令行参数</param>
        /// <param name="setupStartOptions">设置启动参数</param>
        /// <param name="setupService">依赖注入（DI）</param>
        /// <param name="setupApp">在<see cref="Microsoft.AspNetCore.Builder.WebApplication.Run(string?)"/>之前执行，做一些初始化的任务</param>
        /// <param name="kestrelServerAction"></param>
        public static void Start(string[] args,
                                 Action<StartOptions>? setupStartOptions = null,
                                 Action<IServiceCollection, IConfigurationRoot, IWebHostEnvironment>? setupService = null,
                                 Action<Microsoft.AspNetCore.Builder.WebApplication>? setupApp = null,
                                 Action<KestrelServerOptions>? kestrelServerAction = null) {
            var timer = System.Diagnostics.Stopwatch.StartNew();
            Console.Write("[");
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.Write($"{timer.Elapsed}");
            Console.ResetColor();
            Console.Write("] ");
            Console.BackgroundColor = ConsoleColor.DarkYellow;
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("启动中...");
            Console.ResetColor();
            var startOptions = new StartOptions();
            var builder = Microsoft.AspNetCore.Builder.WebApplication.CreateBuilder(args);
            Environment = builder.Environment;
            Console.WriteLine($"ROOT:{Environment.ContentRootPath}");
            Console.WriteLine($"ENV:{Environment.EnvironmentName}");
            if (kestrelServerAction != null) {
                builder.WebHost.UseKestrel(kestrelServerAction);
            }
            setupStartOptions?.Invoke(startOptions);
            var configuration = builder.Configuration// new ConfigurationBuilder()
                .AddInMemoryCollection()
                .SetBasePath(Environment.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
                //.AddJsonFile($"config/appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build() ?? builder.Configuration;

            var services = Configure(builder.Services, configuration, startOptions, setupService);

            var app = builder.Build();
            app.Lifetime.ApplicationStarted.Register(() => {
                Console.Write("[");
                Console.ForegroundColor = ConsoleColor.DarkGray;
                Console.Write($"{timer.Elapsed}");
                Console.ResetColor();
                Console.Write("] ");
                Console.BackgroundColor = ConsoleColor.DarkGreen;
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("启动完成。");
                Console.ResetColor();
            });
            app.Lifetime.ApplicationStopped.Register(() => {
                Console.Write("[");
                Console.ForegroundColor = ConsoleColor.DarkGray;
                Console.Write($"{timer.Elapsed}");
                Console.ResetColor();
                Console.Write("] ");
                Console.BackgroundColor = ConsoleColor.DarkRed;
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("停止完成。");
                Console.ResetColor();
            });
            // Configure the HTTP request pipeline.

            app.UseSession();
            app.UseRouting()
               .UseAuthentication()
               .UseAuthorization()
               .UseCustomSwaggerUI()
               .UseCustomElmah()
               .UseCors(builder => builder.WithOrigins("*").AllowAnyHeader().AllowAnyMethod())
               .SetupRobots();
            app.MapControllers();
            if (HasHealthCheck) {
                app.MapHealthChecks("/healthz/alive", new HealthCheckOptions {
                    Predicate = healthCheck => healthCheck.Tags.Contains("alive"),
                });
                app.MapHealthChecks("/healthz/ready", new HealthCheckOptions {
                    Predicate = healthCheck => healthCheck.Tags.Contains("ready")
                });
            }
            app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedHost | ForwardedHeaders.XForwardedProto });
            setupApp?.Invoke(app);
            Console.Write("[");
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.Write($"{timer.Elapsed}");
            Console.ResetColor();
            Console.Write("] ");
            Console.BackgroundColor = ConsoleColor.DarkCyan;
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("设置完成。");
            Console.ResetColor();
            app.Run();
            timer.Stop();
        }
        static IServiceCollection Configure(IServiceCollection services,
                                            IConfigurationRoot configuration,
                                            StartOptions startOptions,
                                            Action<IServiceCollection, IConfigurationRoot, IWebHostEnvironment>? configureServiceAction = null) {
            services.AddHttpContextAccessor();
            services.SetupApiVersioning();

            // Add services to the container.
            services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            services.AddEndpointsApiExplorer();

            configureServiceAction?.Invoke(services, configuration, Environment);

            ////TODO:阿里
            //services.AddLogging(c => c.AddAliLogger());

            services.Configure<RedisSettings>(configuration.GetSection("AppSettings"));
            services.SetupConfigurations(configuration)
                    .Configure<JwtSettings>()
                    .Configure<ApiCallerSettings>();

            services.AddHttpClient();

            #region 压缩 Compression
            if (startOptions.EnableResponseCompression) {
                services.AddResponseCompression(options => {
                    //可以添加多种压缩类型，程序会根据级别自动获取最优方式
                    options.Providers.Add<BrotliCompressionProvider>();
                    options.Providers.Add<GzipCompressionProvider>();
                    //添加自定义压缩策略
                    //options.Providers.Add<MyCompressionProvider>();
                    //针对指定的MimeType来使用压缩策略
                    options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json" });
                });
                //针对不同的压缩类型，设置对应的压缩级别
                services.Configure<GzipCompressionProviderOptions>(options => {
                    //使用最快的方式进行压缩，单不一定是压缩效果最好的方式
                    options.Level = startOptions.CompressionLevel;

                    //不进行压缩操作
                    //options.Level = CompressionLevel.NoCompression;

                    //即使需要耗费很长的时间，也要使用压缩效果最好的方式
                    //options.Level = CompressionLevel.Optimal;
                });
            }
            #endregion
            #region MVC
            services.AddSession(options => {
                // Set a short timeout for easy testing.
                options.IdleTimeout = TimeSpan.FromDays(7);
                options.Cookie.HttpOnly = true;
            });
            services.SetupMvc();
            #endregion
            services.AddSingleton<RedisClient>();
            services.SetupJwt(configuration);

            //TODO:
            //services.AddSingleton<AliLogger>();
            if (startOptions.AutoRegisterServices) {
                services.BeginRegisterServices(startOptions.GetAssemblies())
                        .RegisterAsSingleton<IAutoRegisterSingleton>()
                        .RegisterAsScoped<IAutoRegisterRepository>();
            }
            return services;
        }
        /// <summary>
        /// 启动参数
        /// </summary>
        public sealed class StartOptions {
            /// <summary>
            /// 是否自动注册服务（默认：true），默认注册入口程序集（<see cref="Assembly.GetEntryAssembly"/>）和WebApi.Library，可以配置（<see cref="AddAssembly(Type[])"/>）其他程序集。
            /// 参考：<see cref="IAutoRegisterRepository"/>、<see cref="IAutoRegisterSingleton"/>
            /// </summary>
            public bool AutoRegisterServices { get; set; } = true;
            /// <summary>
            /// 开启相应压缩（默认：true）
            /// </summary>
            public bool EnableResponseCompression { get; set; } = true;
            /// <summary>
            /// 响应压缩级别（默认：<see cref="System.IO.Compression.CompressionLevel.Fastest"/>）
            /// </summary>
            public System.IO.Compression.CompressionLevel CompressionLevel { get; set; } = System.IO.Compression.CompressionLevel.Fastest;

            private readonly List<Assembly?> assemblies = new() { Assembly.GetEntryAssembly(), typeof(WebApplication).Assembly };
            internal Assembly?[] GetAssemblies() => assemblies.Distinct().ToArray();
            /// <summary>
            /// 添加需要自动注册的程序集
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <returns></returns>
            public StartOptions AddAssembly<T>() {
                assemblies.Add(typeof(T).Assembly);
                return this;
            }
            /// <summary>
            /// 添加需要自动注册的程序集
            /// </summary>
            /// <param name="types"></param>
            /// <returns></returns>
            public StartOptions AddAssembly(params Type[] types) {
                assemblies.AddRange(types.Select(t => t.Assembly));
                return this;
            }
        }
    }// end class
}
